#include "juego.h"

namespace aed2 {

Juego::Juego(const Tablero<Movimiento> & tb, const Vector<Casilla> & cs,
	const Vector<Mision> & ms) : tablero(tb), jugadores(cs.Longitud()),
	jugadoresConFichas(0, cs.Longitud()	- 1),
	fichasPorCasilla(tb.Casillas().Cardinal()) {

	assert(cs.Longitud() > 0);
	assert(cs.Longitud() == ms.Longitud());

	Conj<Casilla> casillasTablero = tb.Casillas();
	Conj<Casilla>::const_Iterador itCas = casillasTablero.CrearIt();
	Conj<Continente> continentesTablero = tb.Continentes();

	for (Nat i = 0; i < cs.Longitud(); i++) {
		Conj<Casilla> cass;
		assert(casillasTablero.Pertenece(cs[i]));
		cass.AgregarRapido(cs[i]);
	}

	for (Nat i = 0; i < ms.Longitud(); i++) {
		Conj<Continente> conts;
		assert(continentesTablero.Pertenece(ms[i]));
		conts.AgregarRapido(ms[i]);
	}

	OrigenesPorCasilla();



	while (itCas.HaySiguiente()) {

		Nat j;
		Casilla c = itCas.Siguiente();

		for (j = 0; j < cs.Longitud(); j++) {
			if (cs[j] == c) {
				break;
			}
		}

		MultConjAcotado fichasCasilla(cs.Longitud());
		if (j < cs.Longitud()) {

			jugadoresConFichas.Agregar(j);

			ConjAcotado casillasDominadas(0, casillasTablero.Cardinal() - 1);
			Nat cantDomMision = 0;

			casillasDominadas.Agregar(cs[j]);
			if (tb.ContinenteDe(cs[j]) == ms[j]) {
				cantDomMision++;
			}

			DescriptorJugador descriptorJugador(1, 0, ms[j], casillasDominadas,
					cantDomMision);

			jugadores.Definir(j, descriptorJugador);

			fichasCasilla.Agregar(j, 1);
		}

		fichasPorCasilla.Definir(c, fichasCasilla);
		itCas.Avanzar();
	}
}

void Juego::AgregarFicha(Jugador jug, Casilla cas) {

	assert(jugadores.Definido(jug));
	assert(jugadoresConFichas.Pertenece(jug));
	assert(Vacia(cas) || ((Dominada(cas) && (Dominador(cas) == jug))));

	//jugadores.Obtener(jug).fichas = jugadores.Obtener(jug).fichas + 1;

	jugadores.Obtener(jug).fichas += 1;
	jugadores.Obtener(jug).fichasAgregadas += 1;
	SumarFichas(jug, cas, 1);
}

void Juego::Mover(Jugador jug, Movimiento mov, Nat cant) {

	assert(jugadores.Definido(jug));
	assert(jugadoresConFichas.Pertenece(jug));

	Conj<Casilla> casillasTablero = tablero.Casillas();

	bool movDefinido = origenesPorCasilla.Definido(mov);
	const DiccAcotado<Conj<Casilla> > *origenesMovimiento;

	if (movDefinido) {
		origenesMovimiento = &(origenesPorCasilla.Obtener(mov));
	}

	Conj<Casilla>::const_Iterador itCas = casillasTablero.CrearIt();

	while (itCas.HaySiguiente()) {

		Casilla c = itCas.Siguiente();
		
		if (Disputada(c)) {
			Jugador perdedor = fichasPorCasilla.Obtener(c).Minimo();
			jugadores.Obtener(perdedor).fichas -= 1;
			RestarFichas(perdedor, c, 1);
			
			if (jugadores.Obtener(perdedor).fichas < 1) //El jugador quedó eliminado
				jugadoresConFichas.Eliminar(perdedor);
		}

		Nat totalQuitados = 0;

		if (cant > 0 && movDefinido && origenesMovimiento->Definido(c)) {

			Conj<Casilla>::const_Iterador itOrigenes =
					origenesMovimiento->Obtener(c).CrearIt();

			while (itOrigenes.HaySiguiente()) {

				Casilla co = itOrigenes.Siguiente();
				//std::cout << "Casilla: " << c << ", Casilla Origen: " << co << std::endl;
				
				if (Dominada(co) && Dominador(co) == jug) {
					//std::cout << "Casilla: " << co << " dominada por Jugador: " << jug << std::endl;
					Nat cantQuitados = RestarFichas(jug, co, cant);
					totalQuitados += cantQuitados;
				}

				itOrigenes.Avanzar();
			}

			//std::cout << "Al jugador: " << jug << " se le van a sumar " << totalQuitados << " fichas, en la casilla " << c << std::endl;
			if (totalQuitados > 0)
				SumarFichas(jug, c, totalQuitados);
		}

		itCas.Avanzar();
	}
}

const Tablero<Movimiento> & Juego::TableroDelJuego() const {
	return tablero;
}

ConjAcotado::const_Iterador Juego::Jugadores() const {
	return jugadores.Claves();
}

MultConjAcotado::const_Iterador Juego::Fichas(Casilla cas) const {
	assert(tablero.Casillas().Pertenece(cas));

	return fichasPorCasilla.Obtener(cas).CrearIt();
}

Mision Juego::MisionDe(Jugador jug) const {
	assert(jugadores.Definido(jug));

	return jugadores.Obtener(jug).mision;
}

Nat Juego::FichasAgregadas(Jugador jug) const {
	assert(jugadores.Definido(jug));

	return jugadores.Obtener(jug).fichasAgregadas;
}

bool Juego::Dominada(Casilla cas) const {
	assert(tablero.Casillas().Pertenece(cas));

	return (fichasPorCasilla.Obtener(cas).CantDistintos() == 1);
}

bool Juego::Vacia(Casilla cas) const {
	assert(tablero.Casillas().Pertenece(cas));

	return (fichasPorCasilla.Obtener(cas).CantDistintos() == 0);
}

bool Juego::Disputada(Casilla cas) const {
	assert(tablero.Casillas().Pertenece(cas));

	return (fichasPorCasilla.Obtener(cas).CantDistintos() > 1);
}

MultConjAcotado::const_Iterador_Unicos Juego::Disputan(Casilla cas) const {
	assert(tablero.Casillas().Pertenece(cas));
	assert(Disputada(cas));

	return fichasPorCasilla.Obtener(cas).Conj();
}

Jugador Juego::Dominador(Casilla cas) const {
	assert(tablero.Casillas().Pertenece(cas));
	assert(Dominada(cas));

	return fichasPorCasilla.Obtener(cas).Minimo();
}

ConjAcotado::const_Iterador Juego::Dominadas(Jugador jug) const {
	assert(jugadores.Definido(jug));

	return jugadores.Obtener(jug).casillasDominadas.CrearIt();
}

Nat Juego::PorDominar(Jugador jug) const {
	assert(jugadores.Definido(jug));

	return tablero.CasillasDe(jugadores.Obtener(jug).mision).Cardinal()
			- jugadores.Obtener(jug).cantDomMision;
}

bool Juego::MisionCumplida(Jugador jug) const {
	assert(jugadores.Definido(jug));

	return tablero.CasillasDe(jugadores.Obtener(jug).mision).Cardinal()
			== jugadores.Obtener(jug).cantDomMision;
}

bool Juego::TieneFichas(Jugador jug) const {
	assert(jugadores.Definido(jug));

	return jugadoresConFichas.Pertenece(jug);
}

ConjAcotado::const_Iterador Juego::ConFichas() const {
	return jugadoresConFichas.CrearIt();
}

Nat Juego::CantidadConFichas() const {
	return jugadoresConFichas.Cardinal();
}

ConjAcotado Juego::Eliminados() const {
	assert(not Finalizado());

	ConjAcotado eliminados(0, jugadoresConFichas.Supremo());
	for (Nat j = 0; j <= jugadoresConFichas.Supremo(); j++) {
		if (!jugadoresConFichas.Pertenece(j)) {
			eliminados.Agregar(j);
		}
	}

	return eliminados;
}

ConjAcotado Juego::CumplieronMision() const {

	ConjAcotado cumplieron(0, jugadoresConFichas.Supremo());
	for (Nat j = 0; j <= jugadoresConFichas.Supremo(); j++) {
		if (MisionCumplida(j)) {
			cumplieron.Agregar(j);
		}
	}

	return cumplieron;
}

ConjAcotado Juego::Ganadores() const {
	assert(Finalizado());

	if (jugadoresConFichas.Cardinal() == 1) {
		return jugadoresConFichas;
	}

	return CumplieronMision();
}

bool Juego::Finalizado() const {
	return jugadoresConFichas.Cardinal() == 1
			|| CumplieronMision().Cardinal() > 0;
}

Conj<Casilla> Juego::Casillas() const {
	return tablero.Casillas();
}

Conj<Continente> Juego::Continentes() const {
	return tablero.Continentes();
}

bool Juego::PuedeAgregar(Jugador jug, Casilla cas) const {

	Conj<Casilla> casillasTablero = tablero.Casillas();

	assert(jugadores.Definido(jug));
	assert(casillasTablero.Pertenece(cas));

	return not Finalizado() && TieneFichas(jug) && (Vacia(cas)
			|| (Dominada(cas) && Dominador(cas) == jug));
}

// OPERACIONES PRIVADAS

Nat Juego::SumarFichas(Jugador jug, Casilla cas, Nat cant) {

	Conj<Casilla> casillasTablero = tablero.Casillas();

	assert(jugadores.Definido(jug));
	assert(casillasTablero.Pertenece(cas));
	assert(cant > 0);

	Nat fichasActuales = fichasPorCasilla.Obtener(cas).Cardinal(jug);
	DescriptorJugador & descJ = jugadores.Obtener(jug);

	if (Vacia(cas)) {

		descJ.casillasDominadas.Agregar(cas);

		if (tablero.ContinenteDe(cas) == descJ.mision) {
			descJ.cantDomMision += 1;
		}

	} else {
		if (Dominada(cas) && Dominador(cas) != jug) {//Estaba dominada por otro jugador
			DescriptorJugador & descJ2 = jugadores.Obtener(Dominador(cas));
			descJ2.casillasDominadas.Eliminar(cas);
			if (tablero.ContinenteDe(cas) == descJ2.mision) {
				descJ2.cantDomMision -= 1;
			}
		}
	}
	fichasPorCasilla.Obtener(cas).Agregar(jug, cant); //Agregar en el multiconjunto de la casilla

	return fichasActuales + cant;
}

Nat Juego::RestarFichas(Jugador jug, Casilla cas, Nat cant) {

	Conj<Casilla> casillasTablero = tablero.Casillas();

	assert(jugadores.Definido(jug));
	assert(casillasTablero.Pertenece(cas));
	assert(cant > 0);

	Nat fichasActuales = fichasPorCasilla.Obtener(cas).Cardinal(jug);
	Nat cantARestar = fichasActuales >= cant ? cant : fichasActuales; // Min(fichasActuales, cant)
	fichasPorCasilla.Obtener(cas).Quitar(jug, cantARestar); //Se quitan la fichas del multiconjunto de la casilla
	DescriptorJugador & descJ = jugadores.Obtener(jug);

	if (fichasActuales - cantARestar < 1) { //El jugador no tiene más fichas en la casilla
		if (Vacia(cas)) {
			descJ.casillasDominadas.Eliminar(cas);
			if (tablero.ContinenteDe(cas) == descJ.mision) {
				//Es una casilla de la misión
				descJ.cantDomMision -= 1;
			}
		} else {

			if (Dominada(cas)) { //Hay un nuevo dominador
				DescriptorJugador & descJ2 = jugadores.Obtener(Dominador(cas));
				descJ2.casillasDominadas.Agregar(cas);
				if (tablero.ContinenteDe(cas) == descJ2.mision) {
					//Es una casilla de la misión
					descJ2.cantDomMision += 1;
				}
			}
		}
	}

	return cantARestar; //Cantidad efectivamente restada
}

void Juego::OrigenesPorCasilla() {
	Conj<Casilla> casillas(tablero.Casillas());
	Conj<Casilla>::const_Iterador itCas = casillas.CrearIt();

	while (itCas.HaySiguiente()) {

		Casilla cas = itCas.Siguiente();
		Tablero<Movimiento>::ItDestinos itDestinos = tablero.Destinos(cas);

		while (itDestinos.HaySiguiente()) {

			Tablero<Movimiento>::CasillaMov casillaMovm = itDestinos.Siguiente();
			Movimiento movimiento = casillaMovm.movimiento;
			Casilla casillaDest = casillaMovm.casilla;

			if (! origenesPorCasilla.Definido(movimiento)) {

				Conj<Casilla> cc;
				cc.AgregarRapido(cas);

				DiccAcotado<Conj<Casilla> > diccOrigenes(
					tablero.Casillas().Cardinal()
				);

				diccOrigenes.Definir(casillaDest, cc);

				origenesPorCasilla.Definir(movimiento, diccOrigenes);
			} else {
				origenesPorCasilla.Obtener(movimiento).Obtener(casillaDest).AgregarRapido(cas);
			}

			itDestinos.Avanzar();
		}

		itCas.Avanzar();
	}
}

} /* FIN NAMESPACE */

